
#ifndef CHANNEL_H
#define CHANNEL_H


#include <sys/types.h>
#include <stdbool.h>


/**
    Type of the channel
*/
typedef enum
{
	UNKNOWN,
	SYNCHRONOUS_SYMMETRIC,
	ASYNCHRONOUS_SYMMETRIC,
	SYNCHRONOUS_ASYMMETRIC,
	ASYNCHRONOUS_ASYMMETRIC,
} ChannelType;

/**
    Communication channel descriptor
*/
typedef struct channel_t* Channel;

/**
    Communicator thread descriptor
*/
typedef struct communicator_t * Communicator;

/**
    Alternative command descriptor
*/
typedef struct alternative_t* Alternative;

/**
    Delegation (of a worker thread to its communicator thread) descriptor
*/
typedef struct delegation_t* Delegation;


// channel functions
/**
    Creates a synchronous communication channel, with a given data size and implemented using
    j additional target variables.
    You may use the function ftok to generate the channel key.
    @param key - the key of the channel
    @param size - the size of the data that will be transmitted on the channel
    @param j - number of the additional target variables that will be used for the implementation
    @return a pointer to the channel descriptor or NULL in case of error (setting errno)
*/
Channel LCcreate_ss( key_t key, size_t size, int j );

/**
    Creates an asynchronous communication channel, with a given data size, asynchrony
    degree and implemented using j additional target variables.
    You may use the function ftok to generate the channel key.
    @param key - the key of the channel
    @param size - the size of the data that will be transmitted on the channel
    @param k - the asynchrony degree
    @param j - number of the additional target variables that will be used for the implementation
    @return a channel descriptor or NULL in case of error (setting errno)
*/
Channel LCcreate_as( key_t key, size_t size, int k, int j);

/**
    This function is used to acquire the communication channel identified by the proper key
    in the addressing space of the caller process.
    @param key - the key of the channel
    @return a pointer to the channel descriptor or NULL in case of error (and set errno)
*/
Channel LCattach( key_t key );


/**
    Destroy the channel. Its relative shared memory segment will be removed from memory
    only when the last process detatches it.

    @param the channel to destroy (the descriptor)
*/
void LCdestroy( Channel ch );

/**
    Removes the channel from the virtual memory of the caller

    @param the channel to detatch (the descriptor)
*/
void LCdetatch( Channel ch );

/**
    Send the message on the given channel.
    This procedure can be blocking if the channel is full (asynchrony degree reached).
    @param kp - the communicator thread descriptor
    @param ch - channel on which send
    @param msg - the message to send
    @return a delegation reference, to check wheter or not the send was completed (and it's
            safe to modify the content of the message)
*/
Delegation LCsend( Communicator kp, Channel ch, char *msg );

/**
    Send a copy of the message on the given channel.
    In this way, it will be possible to modify the message sooner with respect to the
    traditional send (this must always been verified with LCwait primitive).
    This procedure can be blocking if the channel is full (asynchrony degree reached).
    @param kp - the communicator thread descriptor
    @param ch - channel on which send
    @param msg - the message to send
    @return a delegation reference, to check wheter or not the send was completed (and it's
            safe to modify the content of the message)
*/
Delegation LCbsend( Communicator kp, Channel ch, char *msg );


/**
    Receive a message from the given channel.
    It can be blocking if the channel is empty.
    @param ch - channel from which receive
    @return the message
*/

char *LCreceive( Channel ch );

/**
    Wait until the send represented by that particular delegation has
    been completed
*/
void LCwait(Delegation dg);


/**
    Return the size of the data type transmittable with the channe;
    @param ch - the channel
    @return the size of the data type
*/
size_t LCgetChannelTypeSize( Channel ch );

/**
    Returns whether or not the channel is ready to receive
    @param ch - the channel
    @return \b true  if we can receive from the channel, \b false otherwise
*/
bool LCreadyToReceive( Channel ch );

/**
    Returns whether or not the channel is ready to send
    @param ch - the channel
    @return \b true  if we can send to the channel, \b false otherwise
*/
bool LCreadyToSend( Channel ch );

/**
    Prints debug informations about the channel
    @param ch - the channel
*/

void printCH( Channel ch );


// alternative functions

/**
    Creates an alternative command with N predicates (guards), for asymmetric channels.
    @param key - the key of the alternative command that has to be created
    @param n - the number of predicates  that it must have
    @return a descriptor for the alternative command or NULL in case of error (and sets errno)
*/
struct alternative_t *LCcreate_alt( key_t key, int n );

/**
    Sets a predicates of a given alternative command (overwriting it if necessary).
    @param alt - the alternative command
    @param id - the id of the predicate
    @param ch - the channel that is identified by this guards
    @param priority - the priority
    @param cond - the predicate
*/

void LCsetAlt( struct alternative_t *alt, int id, Channel ch, int priority, bool cond );


/**
    Execute the alternative command. If a guard is verified and it is choosen for a receive
    it will returns its id and a reference to the relative channel (where we can then execute a receive).
    @param alt - the alternative command
    @param **ch - a pointer to a channel descriptor. At the end of the procedure it will reference the channel
        choosen by the alternative command or NULL in case of a skip.
    @return the id of the choosen guard. -1 if all the predicates are false.
*/
int LCpoll( struct alternative_t *alt, Channel *ch );

// Communication processor functions
/**
    This function will create a communicator thread that will be scheduled on the same
    core where the caller resides.

    @param core_mapping: an integer vector whose size is equal to the number of logic processor
        present in the machine. core_mapping[i], where i is the processor ID of the worker thread,
        indicates the ID of the logical processor where the communicator thread must be scheduled in
        order to be on the same core of the partner
    @return a communicator_t structure containing the necessary infos or NULL in case of error
*/


Communicator initCommunicator(int core_mapping[]);


/**
    This function will stop the communicator and will wait for its termination.
    This will happen only when all the delegated send are completed.
*/
void finalizeCommunicator(Communicator kp);


#endif

